package com.homedo.microservice.open.service.gateway.manager.web.task;


import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.homedo.common.dao.conf.DbConf;
import com.homedo.common.dao.conf.DruidConf;
import com.homedo.common.dao.util.DruidDataSourceUtil;
import com.homedo.microservice.open.service.gateway.manager.web.bean.po.DbSource;
import com.homedo.microservice.open.service.gateway.manager.web.persistence.dao.DbSourceDao;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

/**
 * 动态更新数据源
 */
@Component
public class DbConfUpdateTask {
    @Autowired
    private DbSourceDao dbSourceDao;
    @Autowired
    private DruidConf druidConf;

    private Map<String, String> md5Caches = Maps.newConcurrentMap();

    private static final Logger LOGGER = LoggerFactory.getLogger(DbConfUpdateTask.class);

    @Scheduled(cron = "0/6 * * * * ?")
    public void dbConfUpdateTask() {

        this.doSchedule();

    }

    private void doSchedule() {
        List<DbSource> dbSources = dbSourceDao.getAll();
        Map<String, DataSource> confsTemp = DbConf.getInstance().getConfsTempRef();

        if (dbSources == null) {
            dbSources = Lists.newArrayList();
        }

        this.removeNotExist(dbSources, confsTemp);
        this.addNew(dbSources, confsTemp);
    }

    private void removeNotExist(List<DbSource> dbSources, Map<String, DataSource> confsTemp) {
        confsTemp.entrySet().forEach(e -> {
            boolean isExist = this.exist(e.getKey(), dbSources);
            if (!isExist) {
                confsTemp.remove(e.getKey());
                DbConf.getInstance().getConfsForceUpdate().remove(e.getKey());
            }
        });
    }

    private boolean exist(String key, List<DbSource> dbSources) {
        final boolean[] isExist = {false};
        dbSources.forEach(e -> {
            String dataSourceName = e.getServiceGatewayName() + "_" + e.getEnv();
            if (dataSourceName.equals(key)) {
                isExist[0] = true;
            }
        });
        return isExist[0];
    }

    private void addNew(List<DbSource> dbSources, Map<String, DataSource> confsTemp) {
        dbSources.forEach(e -> {
            try {
                String dataSourceName = e.getServiceGatewayName() + "_" + e.getEnv();

                String md5 = this.getDbSourceMd5(e);
                boolean isChange = this.isDbSourceChanged(dataSourceName, md5);

                if (confsTemp.get(dataSourceName) == null || isChange) {
                    this.dealDbSource(e, confsTemp);
                }
                if (isChange) {
                    DbConf.getInstance().getConfsForceUpdate().put(dataSourceName, true);
                }
                md5Caches.put(dataSourceName, md5);
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
        });
    }

    private boolean isDbSourceChanged(String dataSourceName, String dbSourceMd5) {
        if (md5Caches.get(dataSourceName) == null) {
            return false;
        }
        return !dbSourceMd5.equals(md5Caches.get(dataSourceName));
    }

    private String getDbSourceMd5(DbSource dbSource) {
        return DigestUtils.md5Hex(JSONObject.toJSONString(dbSource));
    }

    private void dealDbSource(DbSource dbSource, Map<String, DataSource> confsTemp) throws SQLException {
        String dataSourceName = dbSource.getServiceGatewayName() + "_" + dbSource.getEnv();

        DataSource dataSource = this.createDataSource(dbSource);
        confsTemp.put(dataSourceName, dataSource);
    }

    private DataSource createDataSource(DbSource dbSource) throws SQLException {
        DruidConf newDruidConf = new DruidConf();
        BeanUtils.copyProperties(druidConf, newDruidConf);
        String url = "jdbc:mysql://" + dbSource.getIp() + ":" + dbSource.getPort() + "/" + dbSource.getDbName() + "?useOldAliasMetadataBehavior=yes&zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8";
        newDruidConf.setUrl(url);
        newDruidConf.setUsername(dbSource.getUsername());
        newDruidConf.setPassword(dbSource.getPassword());

        return DruidDataSourceUtil.init(DruidDataSourceUtil.createDruidDataSource(newDruidConf));
    }

}
